package top.lingkang.finalgateway.config;

import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import top.lingkang.finalgateway.core.RequestHandler;
import top.lingkang.finalgateway.core.StaticFile;
import top.lingkang.finalgateway.monitor.IpListCheck;
import top.lingkang.finalgateway.monitor.MonitorInduction;
import top.lingkang.finalgateway.utils.CommonUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author lingkang
 * Created by 2024/5/5
 */
@Data
@Slf4j
public class RouterConfig {
    private boolean enable = true;
    // http网关监听端口
    private int port = 9595;
    // http网关转发最大内容，默认 `3145728`（3MB）
    private int maxContentLength = 3145728;
    // 网关转发工作的最大线程数，为 0 时使用默认值：默认`64`(Linux)、`16`(window)。
    private int threadNumber = 0;
    // 调度线程数，一般为1，通常不超过cpu内核数
    private int dispatchThread = 1;
    // ssl的名称
    private String ssl;
    // 只有在ssl启用时不为空
    private String sslAddress;
    // 默认60秒超时时间
    private long timeout=60000L;

    // pass
    private List<PassItem> passHttp = new ArrayList<>();
    private List<PassItem> passWs = new ArrayList<>();

    // accessCheck
    private AccessCheck accessCheck=new AccessCheck();

    // static dist
    private List<StaticDist> staticDist = new ArrayList<>();

    // 其他
    private transient Map<String, RequestHandler> handlerMap;
    private transient DefaultHttpDataFactory httpDataFactory;
    private transient OkHttpClient client;
    private transient MonitorInduction monitor;
    private transient IpListCheck ipListCheck;

    public RequestHandler convertHttp(String uri) {
        RequestHandler handler = new RequestHandler();
        // 解析转发
        if (!passHttp.isEmpty()) {
            for (PassItem passItem : passHttp) {
                if (!uri.startsWith(passItem.getSource())) {
                    continue;
                }
                handler.setType(RouterType.PROXY_PASS);
                // /api /api/index.html --> /index.html
                String result = CommonUtils.getPathWithinPattern(passItem.getSource(), uri);
                // 提前缓存 NextTarget
                List<NextTarget> targets = new ArrayList<>();
                for (String item : passItem.getTarget()) {
                    NextTarget target = new NextTarget();
                    target.setTarget(item + result);
                    target.setHost(CommonUtils.getURL(item).getHost());
                    targets.add(target);
                }
                handler.setNextTargets(targets.toArray(new NextTarget[0]));
                return handler;
            }
        }

        // 解析静态资源
        if (!staticDist.isEmpty()) {
            for (StaticDist staticDist : staticDist) {
                if (!staticDist.isEnable())
                    continue;
                if (!"/".equals(staticDist.getPath())) {
                    if (!uri.startsWith(staticDist.getPath())
                            || !uri.substring(staticDist.getPath().length()).startsWith("/")) {
                        continue;
                    }
                }
                File file = new File(staticDist.getRoot());
                if (!file.exists()) {
                    log.warn("不存在的 root 目录：{}", staticDist.getRoot());
                    continue;
                }

                handler.setType(RouterType.DIST);
                handler.setUri(uri);
                handler.setFileCache(staticDist.isFileCache());
                String url = staticDist.getPath().length() == 1 ? uri : uri.substring(staticDist.getPath().length());
                int indexOf = url.indexOf("?");
                if (indexOf != -1)
                    url = url.substring(0, indexOf);
                // 指向静态文件
                if (url.contains(".")) {
                    StaticFile index = new StaticFile(file.getAbsolutePath() + File.separator + url);
                    if (index.exists()) {
                        handler.setFile(index);
                        return handler;
                    }
                }
                if (!staticDist.getMode().equals("vue")) {
                    return handler;
                }

                // 转发到 index
                StaticFile index = new StaticFile(file.getAbsolutePath() + File.separator + staticDist.getIndex());
                if (!index.exists()) {
                    log.warn("不存在的index文件: {}\n请求url: {}\n配置: {}", index.getAbsolutePath(), uri, staticDist);
                    continue;
                }
                handler.setFile(index);
                return handler;
            }
        }
        handler.setType(RouterType.EMPTY);
        return handler;
    }

    public RequestHandler convertWs(String uri) {
        RequestHandler handler = new RequestHandler();
        handler.setUri(uri);
        handler.setType(RouterType.EMPTY);
        for (PassItem passItem : passWs) {
            if (!uri.startsWith(passItem.getSource())) {
                continue;
            }
            // /ws /ws/chat --> /chat
            String result = CommonUtils.getPathWithinPattern(passItem.getSource(), uri);
            // 提前缓存 NextTarget
            List<NextTarget> targets = new ArrayList<>();
            for (String item : passItem.getTarget()) {
                NextTarget next = new NextTarget();
                next.setHost(CommonUtils.getWsHost(item));
                next.setTarget(item + result);
                targets.add(next);
            }
            handler.setNextTargets(targets.toArray(new NextTarget[0]));
            handler.setType(RouterType.PROXY_WEBSOCKET);
        }
        return handler;
    }

    public NextTarget nextTarget(RequestHandler handler) {
        NextTarget target = handler.getNextTargets()[handler.getNext().get()];
        if (handler.getNextTargets().length > 1) {
            if (handler.getNext().get() == handler.getNextTargets().length - 1) {
                handler.getNext().getAndSet(0);
            } else {
                handler.getNext().incrementAndGet();
            }
        }
        return target;

    }
}
